//#define USE_OLD_SYSTEM

using System;
using SharpMud.Net.Telnet.ANSI;

namespace SharpMud.Net.Telnet.Processors.TextFormatting
{
    public class OutputLayer : System.IO.IDataProcessor
    {
        private System.Collections.IQueue<byte> _ParsedDataQueue;
        private System.Collections.IQueue<byte> _UnparsedDataQueue;
        private TextFormattingLayer _HostLayer;
        private Text.Formatting.FormatTokenizationList _FormatTokenizationList;

#if USE_OLD_SYSTEM
        private string[,]									_ColorChart;
		private int											_MaxCodeLength;
        private System.Text.StringBuilder _Temp;
#else
        private string _Temp;
#endif


        #region Constructors
        private OutputLayer()
        {
        }

        internal OutputLayer(TextFormattingLayer hostLayer)
        {
            _HostLayer = hostLayer;
            
            // _FormatTokenizationList = _HostLayer.HostSession.FormatTokenizationList;
            _FormatTokenizationList = GlobalTokenizationList.TokenizationList;

#if USE_OLD_SYSTEM
            _Temp = new System.Text.StringBuilder();
			_ColorChart = new string[,] {
			{	"[black]"	,	Utility.SetForegroundColor(Color.Black)	},
			{	"[blue]"	,	Utility.SetForegroundColor(Color.Blue)	},
			{	"[cyan]"	,	Utility.SetForegroundColor(Color.Cyan )	},
			{	"[green]"	,	Utility.SetForegroundColor(Color.Green )},
			{	"[purple]"	,	Utility.SetForegroundColor(Color.Magenta )},
			{	"[red]"		,	Utility.SetForegroundColor(Color.Red	)},
			{	"[white]"	,	Utility.SetForegroundColor(Color.White	)},
			{	"[yellow]"	,	Utility.SetForegroundColor(Color.Yellow	)},
			{	"[default]"	,	Utility.SetBackgroundColor(Color.Default )},

			{	"[#black]"	,	Utility.SetBackgroundColor(Color.Black)	},
			{	"[#blue]"	,	Utility.SetBackgroundColor(Color.Blue)	},
			{	"[#cyan]"	,	Utility.SetBackgroundColor(Color.Cyan )	},
			{	"[#green]"	,	Utility.SetBackgroundColor(Color.Green )},
			{	"[#purple]"	,	Utility.SetBackgroundColor(Color.Magenta )},
			{	"[#red]"		,	Utility.SetBackgroundColor(Color.Red	)},
			{	"[#white]"	,	Utility.SetBackgroundColor(Color.White )},
			{	"[#yellow]"	,	Utility.SetBackgroundColor(Color.Yellow )},
			{	"[#default]"	,	Utility.SetBackgroundColor(Color.Default )},
			
			{	"^."		,	Utility.SetTextAttributes(ExtendedTextAttribute.Normal		)},
			{	"^b"		,	Utility.SetTextAttributes(ExtendedTextAttribute.Bold		)},
			{	"^u"		,	Utility.SetTextAttributes(ExtendedTextAttribute.Underline	)},
			{	"^i"		,	Utility.SetTextAttributes(ExtendedTextAttribute.Italic		)},
			{	"^%"		,	Utility.SetTextAttributes(ExtendedTextAttribute.ReverseVideo)},
			{	"^?"		,	Utility.SetTextAttributes(ExtendedTextAttribute.Invisible	)},
			{	"^|"		,	Utility.SetTextAttributes(ExtendedTextAttribute.Blink		)},

			{	"^!"		,	Session.NewLine							},
			{	"^["		,	"["										},
			{	"^]"		,	"]"										},
			{	"^{"		,	"{"										},
			{	"^}"		,	"}"										}
			};

			lock(_ColorChart)
			{
				for(int x=0; x<_ColorChart.GetUpperBound(0); x++)
				{
					if(this._MaxCodeLength < ((string)_ColorChart[x,0]).Length )
						this._MaxCodeLength = ((string)_ColorChart[x,0]).Length;
				}
            }
#else
            _Temp = "";
#endif

        }
        #endregion

        #region Public Properties
        public TextFormattingLayer HostLayer
        {
            get
            {
                return _HostLayer;
            }
        }
        public Net.Telnet.Session.TelnetSessionBase Session
        {
            get
            {
                return HostLayer.HostSession;
            }
        }
        #endregion

        #region IDataProcessor Members

        public System.Collections.IQueue<byte> OutputQueue
        {
            get
            {
                return this._ParsedDataQueue;
            }
        }

        public System.Collections.IQueue<byte> InputQueue
        {
            get
            {
                return this._UnparsedDataQueue;
            }
        }

        public void Initialize(System.Collections.IQueue<byte> inputQueue, System.Collections.IQueue<byte> outputQueue)
        {
            this._UnparsedDataQueue = inputQueue;
            this._ParsedDataQueue = outputQueue;
        }

        public void Pulse()
        {
            lock (_UnparsedDataQueue)
            {
                lock (_ParsedDataQueue)
                {
                    lock (_Temp)
                    { 
                        lock (_FormatTokenizationList)
                        {
                            System.Text.Encoding e = System.Text.Encoding.ASCII;
                            const long timeout = 500; // half a second
                            long time = System.DateTime.Now.Ticks;
                            while (this._UnparsedDataQueue.Count > 0)
                            {
#if !USE_OLD_SYSTEM
                                _Temp = _Temp + (e.GetString(_UnparsedDataQueue.Dequeue(_UnparsedDataQueue.Count)));
                                if ((DateTime.Now.Ticks - time) >= timeout)
                                    break;
                            }
                            Text.Matching.IStringMatcherMatch match = null;
                            Text.Matching.IStringMatcherMatch new_match = null;
                            Text.Formatting.ITextFormatEffect match_effect = null;
                            while (_Temp.Length > 0)
                            {
                                foreach (Text.Formatting.FormatTokenization ft in _FormatTokenizationList.FormatTokenizations)
                                {
                                    foreach (Text.Matching.StringMatcher sm in ft.Tokens)
                                    {
                                        new_match = sm.Match(_Temp);
                                        if (new_match!=null && new_match.Index>=0)
                                        {
                                            if(match==null)
                                            {
                                                match = new_match;
                                                match_effect = (Text.Formatting.ITextFormatEffect)ft.GetEffectInstance();
                                            }
                                            else if((new_match.Index<match.Index))
                                            {
                                                match = new_match;
                                                match_effect = (Text.Formatting.ITextFormatEffect)ft.GetEffectInstance();
                                            }
                                            new_match=null;
                                            continue;
                                        }
                                        else
                                            continue;
                                    }
                                }
                                if (match != null && match.Index >= 0 && match_effect != null)
                                {
                                    //Take the earliest match found, pass-along text leading to it,
                                    //then pass along a rendering of the match, and then keep going

                                    _ParsedDataQueue.Enqueue(e.GetBytes(_Temp.Substring(0, match.Index)));
                                    _ParsedDataQueue.Enqueue(e.GetBytes(match_effect.Render(this._HostLayer.HostSession)));

                                    _Temp = _Temp.Substring(match.Index + match.Length);

                                    match = null;
                                    match_effect = null;
                                }
                                else
                                {
                                    //No matches were found at all, so pass-along the whole string
                                    _ParsedDataQueue.Enqueue(e.GetBytes(_Temp));
                                    _Temp = "";

                                    match = null;
                                    match_effect = null;
                                }
                            }
#else

				if(this._Temp.Length==0)
				{
					char c1 = e.GetChars(new byte[] {_UnparsedDataQueue.Peek() })[0];
					if (c1=='^' && this._UnparsedDataQueue.Count>1 )
					{
						string sc = e.GetString( _UnparsedDataQueue.Dequeue(2) );
						this._ParsedDataQueue.Enqueue( e.GetBytes( Lookup(sc) ) );
						continue;
					}
					if( c1=='[' )
					{
						_UnparsedDataQueue.Dequeue();
						this._Temp.Append(c1);
						continue;
					}
					else
					{
						this._ParsedDataQueue.Enqueue( _UnparsedDataQueue.Dequeue() );
					}
				}
				else
				{
					char c1 = e.GetChars(new byte[] {_UnparsedDataQueue.Peek() })[0];
					if( c1==']' )
					{
						_UnparsedDataQueue.Dequeue();
						this._Temp.Append(c1);
						this._ParsedDataQueue.Enqueue( e.GetBytes( Lookup(this._Temp.ToString() ) ) );
						this._Temp = new System.Text.StringBuilder();
						continue;
					}
					else
					{
						_UnparsedDataQueue.Dequeue();
						this._Temp.Append(c1);
						if( _Temp.Length > this._MaxCodeLength )
						{
							this._ParsedDataQueue.Enqueue( e.GetBytes(_Temp.ToString()));
							this._Temp = new System.Text.StringBuilder();
							continue;
						}
					}					
				}
			}
#endif
                        }
                    }
                }
            }
        }
#if USE_OLD_SYSTEM
		private string Lookup(string code)
		{
			for(int x=0;x<_ColorChart.GetUpperBound(0);x++)
			{
				if(code.ToLower()==_ColorChart[x,0].ToLower())
				{
					return _ColorChart[x,1];
				}
			}
			return "";
		}
#endif

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            //Nothing to dispose
        }

        #endregion
    }
}
